#include "hook.h"
#include "IOmanager.h"
#include "fiber.h"
#include "log.h"
#include "fd_manager.h"
#include "config.h"
#include "timer.h"

#include <cstdint>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>


framework::Logger::ptr g_logger = FRAMEWORK_LOG_NAME("system");

namespace framework
{
static framework::ConfigVar<int>::ptr g_tcp_connect_timeout =
    framework::Config::Lookup("tcp.connect.timeout", 5000, "tcp connect timeout");

//当前线程是否hook
static thread_local bool t_hook_enable = false;

#define HOOK_FUN(XX) \
    XX(sleep)        \
    XX(usleep)       \
    XX(nanosleep)    \
    XX(socket)       \
    XX(connect)      \
    XX(accept)       \
    XX(read)         \
    XX(readv)        \
    XX(recv)         \
    XX(recvfrom)     \
    XX(recvmsg)      \
    XX(write)        \
    XX(writev)       \
    XX(send)         \
    XX(sendto)       \
    XX(sendmsg)      \
    XX(close)        \
    XX(fcntl)        \
    XX(ioctl)        \
    XX(getsockopt)   \
    XX(setsockopt)


void hook_init()
{
    static bool is_inited = false;
    //如何不使用hook,则使用原始库函数
    if(is_inited)
    {
        return;
    }

//如果使用hook,就重写库函数
//此后调用sleep会调用自己覆写的函数，使用sleep_f会调用原始的sleep库函数
#define XX(name) name ## _f = (name ## _fun)dlsym(RTLD_NEXT, #name);
    //sleep_f = (sleep_fun)dlsym(RTLD_NEXT, "sleep");
    HOOK_FUN(XX);
#undef XX
}

static uint64_t s_connect_timeout = -1;
struct _HookIniter
{
    _HookIniter()
    {
        hook_init();

        s_connect_timeout = g_tcp_connect_timeout->getValue();

        g_tcp_connect_timeout->addListener([](const int& old_value, const int& new_value){
                FRAMEWORK_LOG_INFO(g_logger) << "tcp connect timeout changed from "
                                         << old_value << " to " << new_value;
                s_connect_timeout = new_value;
        });
    }
};

//目的是为了让执行main函数之前执行hook_init()
struct _HookIniter s_hook_initer;

//当前线程是否hook
bool is_hook_enable()
{
    return t_hook_enable;
}

//设置当前线程的hook状态
void set_hook_enable(bool flag)
{
    t_hook_enable = flag;
}

}



//条件定时器的条件
struct timer_info
{
    int cancelled = 0; //条件状态
};


//fd: 文件描述符
//fun: 系统库原函数
//hook_fun_name: 要hook的函数名称
//event: 事件类型
//timeout_so: 超时类型（发送超时 - 接收超时）
//arg: 模板变参 Args 是一个模板参数包，而 && 表示右值引用，用于保持参数的值类别
template<typename OriginFun, typename... Args>
static ssize_t do_io(int fd, OriginFun fun, const char* hook_fun_name, 
        uint32_t event, int timeout_so, Args&&... arg)
{
    /*
    std::forward 是 C++ 标准库中的一个模板函数，用于实现完美转发（perfect forwarding）。
    它的主要作用是在函数模板内部将参数按原样（包括引用类型和值类型）转发给其他函数，
    保持参数的值类别和左值/右值属性不变，以实现更精确的函数调用。
    */

    //如果不使用hook
    if(!framework::t_hook_enable)
    {
        //调用原始库函数
        return fun(fd, std::forward<Args>(arg)...);
    }

    //获取文件描述符属性
    framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd);
    if(!ctx)
    {
        return fun(fd, std::forward<Args>(arg)...);
    }

    //如果文件描述符已经关闭
    if(ctx->isClose())
    {
        errno = EBADE;
        return -1;
    }

    //如果不是socket,或者用户主动设置了非阻塞
    if(!ctx->isSocket() || ctx->getUserNonblock())
    {
        return fun(fd, std::forward<Args>(arg)...);
    }

    //获取超时时间
    uint64_t to = ctx->getTimeout(timeout_so);
    //创建条件定时器的条件
    std::shared_ptr<timer_info> tinfo(new timer_info);

retry:
    ssize_t n = fun(fd, std::forward<Args>(arg)...);
    while(n == -1 && errno == EINTR) //如果被系统调用中断
    {
        n = fun(fd, std::forward<Args>(arg)...);
    }

    if(n == -1 && errno == EAGAIN) //如果fd 资源暂时不可用
    {
        framework::IOManager* iom = framework::IOManager::GetThis();
        framework::Timer::ptr timer;
        std::weak_ptr<timer_info> winfo(tinfo);

        //如果还没到 fd 的超时时间 
        if(to != (uint64_t)-1)
        {
            //添加超时条件定时器
            timer = iom->addConditionTimer(to, [winfo, fd, iom, event](){
                auto t = winfo.lock();
                //如果智能指针已经销毁 或者 条件已经取消
                if(!t || t->cancelled)
                {
                    return;
                }
                //设置条件为超时
                t->cancelled = ETIMEDOUT;
                //取消事件  唤醒协程
                iom->cancelEvent(fd,(framework::IOManager::Event)(event));
            }, winfo);
        }

        int rt = iom->addEvent(fd, framework::IOManager::Event(event));
        if(rt)
        {
            FRAMEWORK_LOG_DEBUG(g_logger) << hook_fun_name << " addEvent("
                << fd << ", " << event << ")";
            if(timer)
            {
                timer->cancel();
            }
            return -1;
        }
        else
        {
            //将当前协程切换到后台,并设置为暂停状态
            //条件定时器超取消事件时唤醒  或者fd又接收到数据会唤醒
            framework::Fiber::YieldToHold();
            if(timer)
            {
                timer->cancel();
            }
            if(tinfo->cancelled)  //如果是通过定时器唤醒的协程,说明该任务已经超时
            {
                errno = tinfo->cancelled;
                return -1;
            }
            goto retry;
        }
    }

    return n;
}




extern "C"
{

//给函数指针付初值
#define XX(name) name ## _fun name ## _f = nullptr;
    HOOK_FUN(XX);
#undef XX

unsigned int sleep(unsigned int seconds)
{
    //如果不使用hook
    if(!framework::t_hook_enable)
    {
        //调用原函数
        return sleep_f(seconds);
    }
    //获取当前协程
    framework::Fiber::ptr fiber = framework::Fiber::GetThis();
    //获取IO协程调度器
    framework::IOManager* iom = framework::IOManager::GetThis();

    using ptr = void(framework::Scheduler::*)(framework::Fiber::ptr, int);
    //将当前协程获取出来，让当前协程在seconds秒之后在执行
    //秒转换为毫秒
    iom->addTimer(seconds * 1000, std::bind((ptr)&framework::IOManager::schedule,iom, fiber, -1));

    //将当前协程切换到后台,并设置为暂停状态
    framework::Fiber::YieldToHold();
    return 0;
}

int usleep(useconds_t seconds)
{
    //如果不使用hook
    if(!framework::t_hook_enable)
    {
        //调用原函数
        return usleep_f(seconds);
    }

    //获取当前协程
    framework::Fiber::ptr fiber = framework::Fiber::GetThis();
    //获取IO协程调度器
    framework::IOManager* iom = framework::IOManager::GetThis();

    using ptr = void(framework::Scheduler::*)(framework::Fiber::ptr, int);
    //将当前协程获取出来，让当前协程在seconds秒之后在执行
    //微妙转换为毫秒
    iom->addTimer(seconds / 1000, std::bind((ptr)&framework::IOManager::schedule,iom, fiber, -1));

    //将当前协程切换到后台,并设置为暂停状态
    framework::Fiber::YieldToHold();
    return 0;
}

int nanosleep(const struct timespec *req, struct timespec *rem)
{
    //如果不使用hook
    if(!framework::t_hook_enable)
    {
        //调用原函数
        return nanosleep_f(req, rem);
    }

    int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 / 1000;
    //获取当前协程
    framework::Fiber::ptr fiber = framework::Fiber::GetThis();
    //获取IO协程调度器
    framework::IOManager* iom = framework::IOManager::GetThis();

    using ptr = void(framework::Scheduler::*)(framework::Fiber::ptr, int);
    iom->addTimer(timeout_ms, std::bind((ptr)&framework::IOManager::schedule,iom, fiber, -1));
    framework::Fiber::YieldToHold();
    return 0;
}

//地址类型  套接字类型  协议类型
int socket(int domain, int type, int protocol)
{
    //如果不使用hook
    if(!framework::t_hook_enable)
    {
        //调用原函数
        return socket_f(domain, type, protocol);
    }

    int fd = socket_f(domain, type, protocol);
    if(fd == -1)
    {
        return fd;
    }

    //添加FDCtx
    framework::FdMgr::GetInstance()->get(fd, true);
    return fd;
}


int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, uint64_t timeout_ms)
{
    if(!framework::t_hook_enable) 
    {
        return connect_f(fd, addr, addrlen);
    }

    framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd);
    if(!ctx || ctx->isClose())
    {
        errno = EBADE;
        return -1;
    }

    if(!ctx->isSocket())
    {
        return connect_f(fd, addr, addrlen);
    }

    if(ctx->getUserNonblock()) {
        return connect_f(fd, addr, addrlen);
    }

    int n = connect_f(fd, addr, addrlen);
    if(n == 0)
    {
        return 0;
    }
    else if(n != -1 || errno != EINPROGRESS) //操作正在进行
    {
        return n;
    }

    framework::IOManager* iom = framework::IOManager::GetThis();
    framework::Timer::ptr timer;
    std::shared_ptr<timer_info> tinfo(new timer_info); //初始化条件
    std::weak_ptr<timer_info> winfo(tinfo);

    if(timeout_ms != (uint64_t)-1)
    {
        timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom](){
            auto t = winfo.lock();
            if(!t || t->cancelled) //如果条件不存在
            {
                return;
            }
            t->cancelled = ETIMEDOUT;
            iom->cancelEvent(fd, framework::IOManager::WRITE);
        }, winfo);
    }

    int rt = iom->addEvent(fd, framework::IOManager::WRITE);
    if(rt == 0)
    {
        framework::Fiber::YieldToHold(); //连接成功或者连接超时唤醒
        if(timer)
        {
            timer->cancel();
        }
        if(tinfo->cancelled)
        {
            errno = tinfo->cancelled;
            return -1;
        }
    }
    else
    {
        if(timer)
        {
            timer->cancel();
        }
        FRAMEWORK_LOG_ERROR(g_logger) << "connect addEvent(" << fd << ", WRITE) error";
    }

    int error = 0;
    socklen_t len = sizeof(int);
    //获取一下连接状态
    if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) {
        return -1;
    }
    if(!error) {
        return 0;
    } else {
        errno = error;
        return -1;
    }
}

int connect(int fd, const struct sockaddr *addr, socklen_t len)
{
    return connect_with_timeout(fd, addr, len, framework::s_connect_timeout);
}

int accept(int fd, struct sockaddr* addr, socklen_t* addr_len)
{
    int ret = do_io(fd, accept_f, "accept", framework::IOManager::READ, SO_RCVTIMEO, addr, addr_len);
    if(ret > 0) //如果连接成功
    {
        //添加FDCtx
        framework::FdMgr::GetInstance()->get(ret, true);
    }

    return ret;
}

ssize_t read(int fd, void *buf, size_t nbytes)
{
    return do_io(fd, read_f, "read", framework::IOManager::READ, SO_RCVTIMEO, buf, nbytes);
}

ssize_t readv(int fd, const struct iovec *iov, int iovcnt) 
{
    return do_io(fd, readv_f, "readv", framework::IOManager::READ, SO_RCVTIMEO, iov, iovcnt);
}

ssize_t recv(int sockfd, void *buf, size_t len, int flags) 
{
    return do_io(sockfd, recv_f, "recv", framework::IOManager::READ, SO_RCVTIMEO, buf, len, flags);
}

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) {
    return do_io(sockfd, recvfrom_f, "recvfrom", framework::IOManager::READ, SO_RCVTIMEO, buf, len, flags, src_addr, addrlen);
}

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) 
{
    return do_io(sockfd, recvmsg_f, "recvmsg", framework::IOManager::READ, SO_RCVTIMEO, msg, flags);
}

ssize_t write(int fd, const void *buf, size_t count) 
{
    return do_io(fd, write_f, "write", framework::IOManager::WRITE, SO_SNDTIMEO, buf, count);
}

ssize_t writev(int fd, const struct iovec *iov, int iovcnt) 
{
    return do_io(fd, writev_f, "writev", framework::IOManager::WRITE, SO_SNDTIMEO, iov, iovcnt);
}

ssize_t send(int s, const void *msg, size_t len, int flags) 
{
    return do_io(s, send_f, "send", framework::IOManager::WRITE, SO_SNDTIMEO, msg, len, flags);
}

ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) 
{
    return do_io(s, sendto_f, "sendto", framework::IOManager::WRITE, SO_SNDTIMEO, msg, len, flags, to, tolen);
}

ssize_t sendmsg(int s, const struct msghdr *msg, int flags) 
{
    return do_io(s, sendmsg_f, "sendmsg", framework::IOManager::WRITE, SO_SNDTIMEO, msg, flags);
}

int close(int fd)
{
    if(!framework::t_hook_enable)
    {
        return close_f(fd);
    }

    framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd, false);
    if(ctx)
    {
        auto iom = framework::IOManager::GetThis();
        if(iom)
        {
            iom->cancelAll(fd); //取消该fd上的所有事件
        }

        framework::FdMgr::GetInstance()->del(fd);
    }

    return close_f(fd);
}

//重写fcntl，主要处理F_SETFL和F_GETFL
int fcntl(int fd, int cmd, ...)
{
    va_list va;         //用于获取变参列表中的值
    va_start(va, cmd);  //初始到参数列表的起始位置

    switch (cmd)
    {
    case F_SETFL:   //设置文件描述符的标志和访问模式
        {
            int flag = va_arg(va, int);
            va_end(va); //清理 va_list 变量
            framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd, false);
            if(!ctx || ctx->isClose() || !ctx->isSocket()) //不是socket
            {
                return fcntl_f(fd, cmd, flag);
            }
            ctx->setUserNonblock(flag & O_NONBLOCK);  //设置 用户是否要非阻塞

            if(ctx->getSysNonblock()) //根据系统设定 设置非阻塞
            {
                flag |= O_NONBLOCK;  //设置非阻塞
            }
            else
            {
                flag &= ~O_NONBLOCK;  //取消非阻塞
            }
            return fcntl_f(fd, cmd, flag);
        }
        break;
    case F_GETFL:   //获取文件描述符的标志和访问模式
        {
            va_end(va);
            int flag = fcntl_f(fd, cmd);
            framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd, false);
            if(!ctx || ctx->isSocket() || !ctx->isSocket())
            {
                return flag;
            }
            if(ctx->getUserNonblock())
            {
                return flag | O_NONBLOCK;
            }
            else
            {
                return flag | ~O_NONBLOCK;
            }
        }
        break;
    case F_DUPFD:
    case F_DUPFD_CLOEXEC:
    case F_SETFD:
    case F_SETOWN:
    case F_SETSIG:
    case F_SETLEASE:
    case F_NOTIFY:    //...为整数的情况
        {
            int flag = va_arg(va, int);
            va_end(va);
            return fcntl_f(fd, cmd, flag);
        }
        break;
    case F_SETLK:
    case F_SETLKW:
    case F_GETLK: // ...为空的情况
        {
            va_end(va);
            return fcntl_f(fd, cmd);
        }
        break;
    case F_GETOWN_EX:
    case F_SETOWN_EX: //...为struct f_owner_exlock
        {
            struct f_owner_exlock* arg = va_arg(va, struct f_owner_exlock*);
            va_end(va);
            return fcntl_f(fd, cmd, arg);
        }
        break;
    default:
        break;
    }

    va_end(va);
    return fcntl_f(fd, cmd);
}


int ioctl(int d, unsigned long int request, ...) {
    va_list va;
    va_start(va, request);
    void* arg = va_arg(va, void*);
    va_end(va);

    //FIONBIO 宏用于设置或获取文件描述符的非阻塞模式标志
    if(FIONBIO == request)
    {
        // 如果操作数为零（假），则 ! 运算符返回 true（真）。
        // 如果操作数为非零（真），则 ! 运算符返回 false（假）。
        bool user_nonblock = !!*(int*)arg;
        framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(d);
        if(!ctx || ctx->isClose() || !ctx->isSocket()) 
        {
            return ioctl_f(d, request, arg);
        }
        //这个函数就是设置一下  用户是否非阻塞bool值
        ctx->setUserNonblock(user_nonblock);
    }
    return ioctl_f(d, request, arg);
}

int getsockopt(int fd, int level, int optname, void *__restrict optval, socklen_t *__restrict optlen)
{
    return getsockopt_f(fd, level, optname, optval, optlen);
}

int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
    if(!framework::t_hook_enable)
    {
        return setsockopt_f(fd, level, optname, optval, optlen);
    }

    if(level == SOL_SOCKET) //如果是关于socket的套接字
    {
        if(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) //如果设置了接收或者发送超时
        {
            framework::FDCtx::ptr ctx = framework::FdMgr::GetInstance()->get(fd, false);
            if(ctx)
            {
                const timeval* v = (const timeval*)optval;  //获取超时时间
                ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000);
            }
        }
    }
    return setsockopt_f(fd, level, optname, optval, optlen);
}

}

